为什么用 apply_patch 而不是 edit

edit 工具适合"改一两处精确文本"。但当你要:

  • 同一个文件改 5 处不相邻的内容
  • 一口气改 3 个文件
  • 在文件中间插入大段新代码

用 edit 就得调用 5~8 次,而且每次都要保证 oldText 唯一不重叠。apply_patch 用 * Begin Patch / * End Patch 的 diff 格式,一次调用把所有改动打包,且天然要求"上下文匹配",错了整体拒绝而非部分生效。

补丁格式速记

*** Begin Patch
*** Update File: path/to/file.py
@@
 def foo():
-    old_line()
+    new_line()
     keep_this()
*** Add File: path/to/new.py
+print("created")
*** Delete File: path/to/old.py
*** End Patch

三种操作:

| 操作 | 写法 | 用途 |

|------|------|------|

| Update File | @@ 区块里 - 删、+ 加 | 改现有文件 |

| Add File | 全 + 行 | 新建文件 |

| Delete File | 单独一行 | 删除文件 |

Update 区块的规则

  • - 行是原内容(必须精确匹配文件现有文本,包括空格)
  • + 行是新内容
  • 不带符号的行是"上下文",用于定位,不参与修改
  • 一个 Update File 里可以有多组 @@ 区块,对应不相邻的多处改动

真实示例:给 3 个脚本统一加日志前缀

假设要给 a.py、b.py、c.py 各加一行 import,并改一处函数体:

*** Begin Patch
*** Update File: scripts/a.py
@@
 import os
+import logging
@@
 def run():
-    print("start")
+    logging.info("start")
*** Update File: scripts/b.py
@@
 import os
+import logging
*** Update File: scripts/c.py
@@
 import os
+import logging
*** End Patch

一次调用,三文件齐改。

关键行为(必须知道)

  1. 上下文不匹配 → 整片拒绝:任何一处 - 行对不上,整个 patch 不生效,不会"改了一半"。这比 edit 多次调用更安全。
  1. 不要用 patch 改二进制:只针对文本文件,图片/压缩包会损坏。
  1. 大段不变内容别抄进 patch:只写改动点附近的上下文即可,减少匹配失败概率。
  1. 相对路径相对于"当前工作区根",建议用绝对路径或仓库相对路径避免歧义。

标准工作流

  1. 先用 read 看清楚现有内容(尤其是要删的 - 行必须一字不差)。
  1. 写 patch:Update 改、Add 建、Delete 删,一次打包。
  1. 看返回:成功会列出改了哪些文件;失败会告诉你哪行上下文不匹配。
  1. 匹配失败就回去重读,别硬改——patch 拒绝是保护,不是 bug。

踩坑与反模式

  • ❌ - 行带多余空格:diff 匹配是逐字符的,多一个空格就整体失败。先 read 原文再抄。
  • ❌ 把 patch 当"全文覆盖":它只改你写的区块,不是重写整个文件。
  • ❌ 跨文件改又想部分成功:patch 是原子性的,要么全成要么全拒,别指望"先改能改的"。
  • ✅ 多文件/多处改动首选 patch;单点精确替换用 edit 更快。

和 edit 的分工

  • edit:1~2 处、上下文清楚、要快速替换 → 直接。
  • apply_patch:3 处以上、跨文件、插入大段 → 打包更安全。

一句话总结

改得多就用 apply_patch:统一 diff 格式、原子生效、错一处全拒——比反复 edit 更稳,也更不容易误伤现有代码。


扫码关注公众号,获取更多 OpenClaw 实操技巧:

公众号二维码